#include<iostream>
#include<cmath>
#include <map>
#include<algorithm>
#include<cstring>

using namespace std;
int binary_to_int[2][2]={0,1,2,3};
int add_round_key_array[16]={0},multiplication_ans[16]={0};

string int_to_binary[16]={"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"};
string decryption_sbox[4][4]={"1010","0101","1001","1011","0001","0111","1000","1111","0110","0000","0010","0011","1100","0100","1101","1110"};

bool myfunction (int i, int j) {
    return (i==j);
}

int binary_to_decimal_for_four_bits(int binary_num)
{
    int final=0,count=0;
    
    while (binary_num>0)
    {
        
        final+=binary_num%10*pow(2,count);
        binary_num=binary_num/10;
        count++;
    }
    
    return final;
}


int * perform_multiplication(int nibbles[],int mul_factor_matrix[2][2],int size)
{
        int S[2][2]={0},S_duplicate[2][2]={0},final[2][2]={0},i,j,k,temp,max;
 
    S[0][0]=binary_to_decimal_for_four_bits(nibbles[0]);
    S[0][1]=binary_to_decimal_for_four_bits(nibbles[2]);
    S[1][0]=binary_to_decimal_for_four_bits(nibbles[1]);
    S[1][1]=binary_to_decimal_for_four_bits(nibbles[3]);
    S_duplicate[0][0]=nibbles[0];
    S_duplicate[0][1]=nibbles[2];
    S_duplicate[1][0]=nibbles[1];
    S_duplicate[1][1]=nibbles[3];

    for(k=0;k<2;k++)
        for(i=0;i<2;i++)
            for(j=0;j<2;j++)
                if((mul_factor_matrix[k][j]*S[j][i])>15)
                {
                    int  divisor[6]={0,1,0,0,1,1},divisor2[7]={0,0,1,0,0,1,1},divident[7]={0},shifted_divisor[7]={0},count=0,temp_number=1000,divident_in_decimal_form=0;
                    int vari,vari1;
                    int divident2[7]={0};

                    map<int,int> flag;

                    
                    temp=S_duplicate[j][i];
                    vari=temp;
                    vari1=temp_number;
                    int vari2;
                    vari2=temp;
                    int vari3;
                    vari3=temp_number;

                    
                    if(size==6)
                    {
                    while((temp)!=0)
                    {
                       
                        if(temp!=0)
                        divident[count]=temp/temp_number;
                        
                        count++;
                        temp=temp%temp_number;

                        temp_number=temp_number/10;

                    }
                        
                        
                        if(divident[0]==1)
                        {
                            shifted_divisor[0]=1;
                            shifted_divisor[1]=0;
                            shifted_divisor[2]=0;
                            shifted_divisor[3]=1;
                            shifted_divisor[4]=1;
                            shifted_divisor[5]=0;
                            
                            for(int counter=0;counter<6;counter++)
                            {
                                divident[counter]=divident[counter]^shifted_divisor[counter];
                            }
                            while(divident[1]!=0)
                                for(int counter=0;counter<6;counter++)
                                {
                                    divident[counter]=divident[counter]^divisor[counter];
                                }
                            
                        }
                        else if(divident[1]==1)
                        {
                            while(divident[1]!=0)
                                for(int counter=0;counter<6;counter++)
                                    divident[counter]=divident[counter]^divisor[counter];
                            
                        }
                        
                        int counter_var=3;
                        for(int numm=2;numm<6;numm++)
                        {
                            
                            divident_in_decimal_form+=divident[numm]*pow(2,counter_var);
                            counter_var--;
                        }
                        
                        //for(int counter=2;counter<6;counter++)
                        final[k][i]=final[k][i]^divident_in_decimal_form;

                    }
                    else
                    {
                        count=0;
                        
                        
                        
                        if(mul_factor_matrix[k][j]==9)
                        {
                          
                            int variable1[7]={0},variable2[7]={0},counter_varr;
                        
                            int another_var;
                            another_var=vari;
                            for(counter_varr=6;vari!=0;counter_varr--)
                            {
                                variable1[counter_varr]=vari%10;
                                vari=vari/10;
                                
                            }
                            flag[100000]=0;
                            
                            vari=another_var;
                            counter_varr++;
                            for(counter_varr=counter_varr;vari!=0;counter_varr--)
                            {
                                if(counter_varr>3&&flag[100000]!=1)
                                {
                                    
                                    if(variable1[counter_varr-1]==0)
                                    {
                                    continue;
                                    }
                                    else
                                    {
                                        counter_varr--;
                                        continue;
                                    }
                                    flag[100000]=1;
                                    
                                }
                                    else
                                variable2[counter_varr]=vari%10;
                                vari=vari/10;
                                
                            }
                            
                            for(counter_varr=0;counter_varr<7;counter_varr++)
                            {
                                divident2[counter_varr]=variable1[counter_varr]^variable2[counter_varr];
                            }
                           
                            
                        label:  if(divident2[0]==1)
                            {
                                
                                shifted_divisor[0]=1;
                                shifted_divisor[1]=0;
                                shifted_divisor[2]=0;
                                shifted_divisor[3]=1;
                                shifted_divisor[4]=1;
                                shifted_divisor[5]=0;
                                shifted_divisor[6]=0;


                                

                                for(int counter=0;counter<7;counter++)
                                {
                                    divident2[counter]=divident2[counter]^shifted_divisor[counter];
                                }
                                
                                goto label;

                                
                            }
                            else if(divident2[1]==1)
                            {
                                shifted_divisor[0]=0;
                                shifted_divisor[1]=1;
                                shifted_divisor[2]=0;
                                shifted_divisor[3]=0;
                                shifted_divisor[4]=1;
                                shifted_divisor[5]=1;
                                shifted_divisor[6]=0;
                                for(int counter=0;counter<7;counter++)
                                {
                                    divident2[counter]=divident2[counter]^shifted_divisor[counter];
                                }
                                goto label;
                                
                            }
                            
                            else if(divident2[2]==1)
                            {
                                for(int counter=0;counter<7;counter++)
                                    {
                                        divident2[counter]=divident2[counter]^divisor2[counter];
 
                                    }
                            }
                            
                            int counter_var=3;
                            
                            divident_in_decimal_form=0;
                            for(int numm=3;numm<7;numm++)
                            {
                                
                                divident_in_decimal_form+=divident2[numm]*pow(2,counter_var);
                                counter_var--;
                            }
                            
                            final[k][i]=final[k][i]^divident_in_decimal_form;

                        
                        }
                        
                        else
                        {
                            vari1=100000;
                            while((vari)!=0)
                            {
                                
                                if(vari!=0)
                                    divident2[count]=vari/vari1;
                                
                                count++;
                                vari=vari%vari1;
                                
                                vari1=vari1/10;
                                
                            }
                            
                            if(divident2[2]==1)
                            {
                                
                                shifted_divisor[0]=0;
                                shifted_divisor[1]=0;
                                shifted_divisor[2]=1;
                                shifted_divisor[3]=0;
                                shifted_divisor[4]=0;
                                shifted_divisor[5]=1;
                                shifted_divisor[6]=1;
                        
                                for(int counter=0;counter<7;counter++)
                                {
                                    divident2[counter]=divident2[counter]^shifted_divisor[counter];
                                }
                                
                            }
                            
                            int counter_var=3;
                            divident_in_decimal_form=0;
                            for(int numm=3;numm<7;numm++)
                            {
                                
                                divident_in_decimal_form+=divident2[numm]*pow(2,counter_var);
                                counter_var--;
                            }
                            
                            final[k][i]=final[k][i]^divident_in_decimal_form;
 
                        
                        }
                 
                    }
                    
                }
    
                else
                final[k][i]=final[k][i]^(mul_factor_matrix[k][j]*S[j][i]);
    
    string answer_in_4_bits[4];
    int var=0;

    
        for(i=0;i<2;i++)
        {
            for(int j=0;j<2;j++)
            {
                answer_in_4_bits[var]=int_to_binary[final[j][i]];
                var++;
            }
        }
    
    
    int final_ans[16],number=0;
    
    
        
            string dec_num= answer_in_4_bits[0];
            for(int variable=0;variable<4;variable++)
            {
                
                final_ans[number] = dec_num[variable]-48;
                number++;
                
                
            }
            dec_num= answer_in_4_bits[1];
            for(int variable=0;variable<4;variable++)
            {
                final_ans[number] = dec_num[variable]-48;

                number++;
                
                
            }
            dec_num= answer_in_4_bits[2];
            for(int variable=0;variable<4;variable++)
            {
                final_ans[number] = dec_num[variable]-48;

                 number++;
                
            }
            dec_num= answer_in_4_bits[3];
            for(int variable=0;variable<4;variable++)
            {
                final_ans[number] = dec_num[variable]-48;

                 number++;
                
            }
    
    for(i=0;i<16;i++)
        multiplication_ans[i]=final_ans[i];
    
    return final_ans;
    
}



int concatenate(int x, int y) {
    unsigned pow = 10;
    while(y >= pow)
        pow *= 10;
    return x * pow + y;        
}

int binary_to_decimal(int num)
{
    int rem,base=1,dec=0;
    while (num > 0)
    {
        rem = num % 10;
        dec = dec + rem * base;
        base = base * 2;
        num = num / 10;
    }
    
    return dec;
}

int * rot_nib(int num)
{
    int i,temp,temp_array[8]={0},final=0;
    
    for(i=0;i<8;i++)
    {
     temp_array[7-i] = num % 10;
     num = num/10;
    }
    
    for(i=0;i<4;i++)
    {
        temp=temp_array[i];
        temp_array[i]=temp_array[i+4];
        temp_array[i+4]=temp;
    }
    
    for(i=0;i<8;i++) {
    
    final=final*10+temp_array[i];
    }
    return temp_array;
    
}

int * add_round_key(int plain_text[16],int key[16])
{
    int plain_text_in_integer_form=0,plain_text_in_integer_form_first_8_bits=0,plain_text_in_integer_form_next_8_bits=0,nibbles[16],temp_array[16],i,temp_num;
    
    for(i=0;i<16;i++)
        plain_text_in_integer_form=10*plain_text_in_integer_form+plain_text[i];
    
    for(i=0;i<16;i++)
    plain_text[i]=plain_text[i]^key[i];
  
    for(i=0;i<4;i++)
    {
        add_round_key_array[i]= nibbles[i]=plain_text[i];
        add_round_key_array[i+4]=nibbles[i+4]=plain_text[i+4];
        add_round_key_array[i+8]=nibbles[i+8]=plain_text[i+8];
        add_round_key_array[i+12]=nibbles[i+12]=plain_text[i+12];
    }
    
    return nibbles;
    
}

int * shift_rows(int nibbles[])
{
    int temp;

    
    for(int i=4;i<8;i++)
    {
       
        temp=nibbles[i];
            nibbles[i]=nibbles[i+8];
        nibbles[i+8]=temp;
   
    }
    
    return nibbles;
}

int * sub_nib(int num,string sbox[4][4])
{
   int i,temp_array[8],final;
    string temp,temp1;
    for(i=0;i<8;i++)
    {
     temp_array[7-i] = num % 10;
     num /= 10;
        
    
    }

    temp=sbox[binary_to_int[temp_array[0]][temp_array[1]]][binary_to_int[temp_array[2]][temp_array[3]]];
    temp1=sbox[binary_to_int[temp_array[4]][temp_array[5]]][binary_to_int[temp_array[6]][temp_array[7]]];
  
    final = stoi(temp+temp1);
    for(i=0;i<8;i++)
    {
        temp_array[7-i] = final % 10;
        final /= 10;
        
        
    }
    return temp_array;
    
}


int * sub_nib_for_16_bits(int temp_array[16],string sbox[4][4])
{
    int i,final;
    string temp,temp1,temp2,temp3;

    temp=sbox[binary_to_int[temp_array[0]][temp_array[1]]][binary_to_int[temp_array[2]][temp_array[3]]];
    temp1=sbox[binary_to_int[temp_array[4]][temp_array[5]]][binary_to_int[temp_array[6]][temp_array[7]]];
    temp2=sbox[binary_to_int[temp_array[8]][temp_array[9]]][binary_to_int[temp_array[10]][temp_array[11]]];
    temp3=sbox[binary_to_int[temp_array[12]][temp_array[13]]][binary_to_int[temp_array[14]][temp_array[15]]];
    
    for(i=0;i<4;i++)
    {
        temp_array[i] = temp[i]-48;
        temp_array[i+4] = temp1[i]-48;
        temp_array[i+8] = temp2[i]-48;
        temp_array[i+12] =temp3[i]-48;
       
    }
    
    return temp_array;
    
}


int main()
{

    int C0[8]={1,0,0,0,0,0,0,0},C1[8]={0,0,1,1,0,0,0,0},i,key[16],temp[8],W[6][8]={0},N=2,count=0,key0[16],key1[16],key2[16],plain_text[16],*ptr,nibbles[16],temp_num,temp_num1,*temporary,*another_temp;
string sbox[4][4]={"1001","0100","1010","1011","1101","0001","1000","0101","0110","0010","0000","0011","1100","1110","1111","0111"};

cout<<"\nEnter the key\n";

for(i=0;i<16;i++)
cin>>key[i];

cout<<"\nEnter the plain text\n";

for(i=0;i<16;i++)
cin>>plain_text[i];


for(i=0;i<8;i++)
{
W[0][i]=key[i];
W[1][i]=key[i+8];
}

for(count=0;count<6;count++)
if(N%2==0)
{
    temp_num=0;
    
    for(i=0;i<8;i++)
        temp_num=temp_num*10+W[N-1][i];
    another_temp= rot_nib(temp_num);
    
    temp_num=0;
    for(i=0;i<8;i++)
       temp_num=temp_num*10+(*(another_temp+i));
    
    temporary= sub_nib(temp_num,sbox);
    
    for(i=0;i<8;i++)
    temp[i]=W[N-2][i] ^ (*(temporary+i));
    
    
    if(count==0)
    for(i=0;i<8;i++)
    W[N][i]=temp[i]^C0[i];
     
    else
    for(i=0;i<8;i++)
    W[N][i]=temp[i]^C1[i];
    
    N++;
     
}

else
{
    for(i=0;i<8;i++)
    W[N][i]=W[N-2][i]^W[N-1][i];
    N++;
}

 

    for(i=0;i<8;i++)
    {
        key0[i]=W[0][i];
        key0[8+i]=W[1][i];
        
        key1[i]=W[2][i];
        key1[8+i]=W[3][i];
        
        
        key2[i]=W[4][i];
        key2[8+i]=W[5][i];
        
    }
    
    ptr= add_round_key(plain_text,key0);
    
    for(i=0;i<16;i++)
        nibbles[i]=*(ptr+i);

    ptr=sub_nib_for_16_bits(nibbles,sbox);
    
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(ptr+i);
    }
    
    temporary=shift_rows(nibbles);
    
    
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(temporary+i);
    }
    
    int nibbles_in_four_bit_form[4]={0};
    
    
    
    for(i=0;i<4;i++)
    {
        nibbles_in_four_bit_form[0]=nibbles_in_four_bit_form[0]*10+nibbles[i];
        nibbles_in_four_bit_form[1]=nibbles_in_four_bit_form[1]*10+nibbles[i+4];
        nibbles_in_four_bit_form[2]=nibbles_in_four_bit_form[2]*10+nibbles[i+8];
        nibbles_in_four_bit_form[3]=nibbles_in_four_bit_form[3]*10+nibbles[i+12];
        
    }
    
    int mul_factor_matrix[2][2]={1,4,4,1};

    ptr= perform_multiplication(nibbles_in_four_bit_form,mul_factor_matrix,6);
    
    for(i=0;i<16;i++)
        plain_text[i]=*(ptr+i);
    
    ptr= add_round_key(plain_text,key1);

    for(i=0;i<16;i++)
        nibbles[i]=*(ptr+i);
    
    ptr=sub_nib_for_16_bits(nibbles,sbox);
        
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(ptr+i);
    }
    
    temporary=shift_rows(nibbles);
    
    
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(temporary+i);
    }

    ptr= add_round_key(nibbles,key2);
    
    for(i=0;i<16;i++)
        nibbles[i]=*(ptr+i);
    

    cout<<"Cipher Text is "<<endl;

    for(i=0;i<16;i++)
        cout<<nibbles[i];
    
    cout<<endl;
    
//    --------------- DECRYPTION STARTS HERE-------------
    
    int * pointer;
    pointer= add_round_key(nibbles,key2);
    
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(pointer+i);
    }
    
    temporary=shift_rows(add_round_key_array);
    
    
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(temporary+i);
    }
    
    ptr=sub_nib_for_16_bits(nibbles,decryption_sbox);
 
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(ptr+i);
    }
 
    pointer= add_round_key(nibbles,key1);
    
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(pointer+i);
    }

    int mul_factor_matrix_for_decryption[2][2]={9,2,2,9};
    
    nibbles_in_four_bit_form[0]=nibbles_in_four_bit_form[1]=nibbles_in_four_bit_form[2]=nibbles_in_four_bit_form[3]=0;
    
    for(i=0;i<4;i++)
    {
        nibbles_in_four_bit_form[0]=nibbles_in_four_bit_form[0]*10+add_round_key_array[i];
        nibbles_in_four_bit_form[1]=nibbles_in_four_bit_form[1]*10+add_round_key_array[i+4];
        nibbles_in_four_bit_form[2]=nibbles_in_four_bit_form[2]*10+add_round_key_array[i+8];
        nibbles_in_four_bit_form[3]=nibbles_in_four_bit_form[3]*10+add_round_key_array[i+12];
        
    }
    ptr= perform_multiplication(nibbles_in_four_bit_form,mul_factor_matrix_for_decryption,7);
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(ptr+i);
    }
    temporary=shift_rows(multiplication_ans);
    
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(temporary+i);
    }
    
    ptr=sub_nib_for_16_bits(nibbles,decryption_sbox);
    
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(ptr+i);
    }
    
    
    pointer= add_round_key(nibbles,key0);
    
    cout<<"Decrypted Text is"<<endl;
    for(i=0;i<16;i++)
    {
        nibbles[i]=*(pointer+i);
        cout<<add_round_key_array[i];
    }
    cout<<endl;
    
return 0;

}

/*
 OUTPUT:-
 
 Enter the key
 0 1 0 0 1 0 1 0 1 1 1 1 0 1 0 1
 
 Enter the plain text
 1 1 0 1 0 1 1 1 0 0 1 0 1 0 0 0
 Cipher Text is
 0010010011101100
 Decrypted Text is
 1101011100101000
 
 */
